#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include<assert.h>
#include <conio.h>
#include <windows.h>
#include<graphics.h>
#include<string.h>
#define ROW 4 /* 行数 */
#define COL ROW /* 列数 */
#define KEY_UP 72 /* 方向键'上'的扫描码码值 */
#define KEY_DOWN 80 /* 方向键'下'的扫描码码值 */
#define KEY_LEFT 75 /* 方向键'左'的扫描码码值 */
#define KEY_RIGHT 77 /* 方向键'右'的扫描码码值 */
#define BLANK 0 /* 空白区域的数字 */
int g_seed = 0; /* 修改随机数种子 */
//产生新的数字
bool GetNum(int(*arr)[COL])
{
	srand((unsigned int)time(NULL) + g_seed);
	g_seed++;
	int row = rand() % ROW;
	int col = rand() % COL;
	int ran = 2;//ran用来确认随机数为2或4
	if (rand() % 5 == 0)
	{
		ran = 4;
	}
	while (arr[row][col] != 0)//当前格子不等于0
	{
		col++;
		if (col == COL)
		{
			row = (row + 1) % ROW;
			col = 0;
		}
	}
	if (arr[row][col] == 0)
	{
		arr[row][col] = ran;
		return true;
	}
	else
		return false;
}
//显示界面
void Show(int(*arr)[COL])
{
	system("CLS");//清除屏幕数据
	//// 绘图窗口初始化
	//initgraph(640, 640);

	//// 读取图片至绘图窗口
	//loadimage(NULL, _T("C:\\Users\\86166\\Desktop\\微信图片_20230524161021.jpg"), 640, 640);
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			printf("%d\t", arr[i][j]);
		}
		printf("\n\n");
	}
	/*_getch();
	closegraph();*/
}
//向左合并和移动函数
//有合并或移动数据返回true，否则返回false
bool MergeLeft(int(*arr)[COL])
{
	for (int i = 0; i < 4; i++)
	{
		//如果出现连续三个相同，先合并靠里面的，也就是说先合并最左边的，所以从左向右循环
		//如果后两个合并后与前一个相同，不会继续和前一个合并
		for (int j = 0; j < 3; j++)
		{//合并
			if (((*(arr + i))[j] == (*(arr + i))[j + 1]) && (*(arr + i))[j + 1] != 0)
			{
				(*(arr + i))[j] *= 2;
				(*(arr + i))[j + 1] = 0;
			}
			else if (j < 2 && (*(arr + i))[j] == (*(arr + i))[j + 2] && (*(arr + i))[j + 1] == 0)
			{//解决出现2 0 2 0->4 0 0 0的问题和0 2 0 2->0 4 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i))[j + 2] = 0;
			}
			else if (j == 0 && (*(arr + i))[j] == (*(arr + i))[j + 3] && (*(arr + i))[j + 1] == 0 && (*(arr + i))[j + 2] == 0)
			{//解决出现2 0 0 2->4 0 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i))[j + 3] = 0;
			}
			/*else
			{
				break;
			}*/
		}
		for (int j = 2; j >= 0; j--)
		{//平移
			if ((*(arr + i))[j] == 0 && (*(arr + i))[j + 1] != 0)
			{
				for (int k = j; k < 3; k++)
				{
					(*(arr + i))[k] = (*(arr + i))[k + 1];
				}
				(*(arr + i))[3] = 0;
			}
		}
	}
	return true;
}

//向上合并函数
bool MergeUp(int(*arr)[COL])
{
	for (int j = 0; j < 4; j++)
	{
		//如果出现连续三个相同，先合并靠里面的，也会是说先合并最左边的，所以从左向右循环
		//如果后两个合并后与前一个相同，不会继续和前一个合并
		for (int i = 0; i < 3; i++)
		{//合并
			if ((*(arr + i))[j] == (*(arr + i + 1))[j] && (*(arr + i + 1))[j] != 0)
			{
				(*(arr + i))[j] *= 2;
				(*(arr + i + 1))[j] = 0;
			}
			else if (i < 2 && (*(arr + i))[j] == (*(arr + i + 2))[j] && (*(arr + i + 1))[j] == 0)
			{//解决出现2 0 2 0->4 0 0 0的问题和0 2 0 2->0 4 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i + 2))[j] = 0;
			}
			else if (i == 0 && (*(arr + i))[j] == (*(arr + i + 3))[j] && (*(arr + i + 1))[j] == 0 && (*(arr + i + 2))[j] == 0)
			{//解决出现2 0 0 2->4 0 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i + 3))[j] = 0;
			}
		}
		for (int i = 2; i >= 0; i--)
		{//平移
			if ((*(arr + i))[j] == 0 && (*(arr + i + 1))[j] != 0)
			{
				for (int k = i; k < 3; k++)
				{
					(*(arr + k))[j] = (*(arr + k + 1))[j];
				}
				(*arr + 3)[j] = 0;
			}
		}
	}
	return true;
}

bool MergeRight(int(*arr)[COL])
{
	for (int i = 0; i < 4; i++)
	{
		//如果出现连续三个相同，先合并靠里面的，也会是说先合并最左边的，所以从左向右循环
		//如果后两个合并后与前一个相同，不会继续和前一个合并
		for (int j = 3; j > 0; j--)
		{//合并
			if (((*(arr + i))[j] == (*(arr + i))[j - 1]) && (*(arr + i))[j - 1] != 0)
			{
				(*(arr + i))[j] *= 2;
				(*(arr + i))[j - 1] = 0;
			}
			else if (j > 1 && (*(arr + i))[j] == (*(arr + i))[j - 2] && (*(arr + i))[j - 1] == 0)
			{//解决出现2 0 2 0->4 0 0 0的问题和0 2 0 2->0 4 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i))[j - 2] = 0;
			}
			else if (j == 3 && (*(arr + i))[j] == (*(arr + i))[j - 3] && (*(arr + i))[j - 1] == 0 && (*(arr + i))[j - 2] == 0)
			{//解决出现2 0 0 2->4 0 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i))[j - 3] = 0;
			}
			/*else
			{
				break;
			}*/
		}
		for (int j = 1; j <= 3; j++)
		{//平移
			if ((*(arr + i))[j] == 0 && (*(arr + i))[j - 1] != 0)
			{
				for (int k = j; k > 0; k--)
				{
					(*(arr + i))[k] = (*(arr + i))[k - 1];
				}
				(*(arr + i))[0] = 0;
			}
		}
	}
	return true;
}

//向下合并函数
bool MergeDown(int(*arr)[COL])
{
	for (int j = 0; j < 4; j++)
	{
		//如果出现连续三个相同，先合并靠里面的，也会是说先合并最左边的，所以从左向右循环
		//如果后两个合并后与前一个相同，不会继续和前一个合并
		for (int i = 3; i > 0; i--)
		{//合并
			if ((*(arr + i))[j] == (*(arr + i - 1))[j] && (*(arr + i - 1))[j] != 0)
			{
				(*(arr + i))[j] *= 2;
				(*(arr + i - 1))[j] = 0;
			}
			else if (i > 1 && (*(arr + i))[j] == (*(arr + i - 2))[j] && (*(arr + i - 1))[j] == 0)
			{//解决出现2 0 2 0->4 0 0 0的问题和0 2 0 2->0 4 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i - 2))[j] = 0;
			}
			else if (i == 3 && (*(arr + i))[j] == (*(arr + i - 3))[j] && (*(arr + i - 1))[j] == 0 && (*(arr + i - 2))[j] == 0)
			{//解决出现2 0 0 2->4 0 0 0的问题
				(*(arr + i))[j] *= 2;
				(*(arr + i - 3))[j] = 0;
			}
		}
		for (int i = 1; i < 4; i++)
		{//平移
			if ((*(arr + i))[j] == 0 && (*(arr + i - 1))[j] != 0)
			{
				for (int k = i; k > 0; k--)
				{
					(*(arr + k))[j] = (*(arr + k - 1))[j];
				}
				(*arr)[j] = 0;
			}
		}
	}
	return true;
}

//根据方向键合并相应的数字
//arr:保存数据的数组,dirce :方向值
bool MergeNum(int(*arr)[COL], int dirce)
{
	bool flg = false;
	switch (dirce)
	{
	case 1:
		flg = MergeLeft(arr);//向左合并
		break;
	case 2:
		flg = MergeUp(arr);//向上合并
		break;
	case 3:
		flg = MergeRight(arr);//向右合并
		break;
	case 4:
		flg = MergeDown(arr);//向下合并
		break;
	default:
		break;
	}
	return flg;
}

// 获得方向键键值函数
int GetButton()
{
	int ch;//保存从键盘读取的值

	while (1)

	{
		if (_kbhit())//有击键发生

		{
			ch = _getch();//获取键盘值,不需要回车
			if (ch == 0xE0)//确定是方向键
			{
				ch = _getch();
				if (ch == KEY_LEFT)
					return 1;
				else if (ch == KEY_UP)
					return 2;
				else if (ch == KEY_RIGHT)
					return 3;
				else if (ch == KEY_DOWN)
					return 4;
				else
					return 0;
			}
		}
	}
	ExMessage m;
	while (peekmessage(&m, EX_KEY))
	{
		if (m.message == WM_KEYDOWN)
		{
			switch (m.vkcode)
			{
			case VK_LEFT:
				return 1;
			case VK_UP:
				return 2;
			case VK_RIGHT:
				return 3;
			case VK_DOWN:
				return 4;
			}
		}
	}
	return 0;
}

//判断游戏是否结束函数
bool IsGameover(int(*arr)[COL])
{
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if ((*arr + i)[j] == 0)
			{
				return false;
			}
			else if (((*(arr + i))[j] == (*(arr + i))[j + 1] && j < 3) || ((*(arr + i))[j] == (*(arr + i + 1))[j] && i < 3))
			{
				return false;
			}
		}
	}
	return true;
}

//运行游戏
void Run(int(*arr)[COL])
{
	int direc; /* 保存方向值 */
	while (1)
	{
		direc = GetButton();//获得方向键键值
		//printf("%d\n", direc);//用于测试
		if (!MergeNum(arr, direc)) //合并和移动数据
		{
			continue;
		}
		if (!GetNum(arr)) /* 游戏结束 */
		{
			return;
		}
		Show(arr);
		if (IsGameover(arr)) //游戏是否结束
		{
			return;
		}
	}
}
//开始游戏
void Start(int(*arr)[COL])
{
	for (int i = 0; i < 2; i++) /* 开始界面必须有两个数字 */
	{

		GetNum(arr);
	}
	Show(arr);
}
int main()
{
	int arr[ROW][COL] = { 0 };
	Start(arr);
	Run(arr);
	printf("Game over!\n");
	return 0;
}